#region License

// This file is part of Grawlix.
// 
// Grawlix is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 3 of
// the License, or (at your option) any later version.
// Grawlix is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public
// License along with Grawlix.  If not, see
// <http://www.gnu.org/licenses/>.

#endregion

using System;
using System.Diagnostics;
using C5;

namespace Grawlix.Application.Gui
{
	/// <summary>
	/// A control made up of several subordinate child controls.
	/// </summary>
	public abstract class CompoundControl : Control
	{
		/// <summary>
		/// The set of children owned by this control.
		/// </summary>
		private readonly HashSet<Control> _children =
			new HashSet<Control>();

		/// <summary>
		/// The set of children owned by this control.
		/// </summary>
		public ICollection<Control> Children
		{
			get { return new GuardedCollection<Control>(_children); }
		}

		/// <summary>
		/// Adds a child control to this control.
		/// </summary>
		/// <param name="child">The child control.</param>
		/// <remarks>The default implementation checks for cycles, then
		/// removes from the previous parent and adds it to the
		/// Children list and sets the child's parent.</remarks>
		/// <exception cref="ArgumentNullException"><c>child</c> is
		/// null.</exception>
		/// <exception cref="InvalidOperationException">Attempt to add
		/// a child control to a control that is contained by the
		/// child.</exception>
		protected virtual void AddChild(Control child)
		{
			if (child == null)
				throw new ArgumentNullException("child");
			if (IsWithin(child))
			{
				throw new InvalidOperationException(
					"Attempt to add a child control to " +
					"a control that is contained by the child. " +
					"Ensure that no cycle exists when adding " +
					"child controls.");
			}
			if (child.Parent != null)
				child.Parent.RemoveChild(child);
			Debug.Assert(child.Parent == null);
			_children.Add(child);
			child.Parent = this;
		}

		/// <summary>
		/// Removes a child from this control.
		/// </summary>
		/// <param name="child">The child control.</param>
		/// <remarks>The default implementation checks for errors, then
		/// removes from the Children list and resets the child's
		/// parent.</remarks>
		/// <exception cref="ArgumentNullException"><c>child</c> is
		/// null.</exception>
		/// <exception cref="InvalidOperationException">Attempt to add
		/// a child control to a control that is contained by the
		/// child.</exception>
		protected virtual void RemoveChild(Control child)
		{
			if (child == null)
				throw new ArgumentNullException("child");
			if (!_children.Contains(child))
			{
				throw new InvalidOperationException(
					"Attempt to remove a child control from " +
					"a control that does not contain the child. " +
					"Ensure that a child control is only removed from " +
					"its parent.");
			}
			Debug.Assert(child.Parent == this);
			_children.Remove(child);
			child.Parent = null;
		}
	}
}